#include <jni.h>
#include <string>
#include <android/log.h>

//  __VA_ARGS__ 代表... 可变参数
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,__VA_ARGS__);
#define  LOG(...) __android_log_print(ANDROID_LOG_ERROR,"CrazyMoJNI",__VA_ARGS__);

extern "C"
JNIEXPORT void JNICALL
Java_com_crazymo_ndk_jni_JNIHelper_sendBasicType(JNIEnv *env, jobject instance, jdouble df, jint k,
                                                 jlong lk, jfloat f, jboolean b, jchar c) {

    LOGE("CrazyMoJNI", "接收Java传递而来的参数：df=%lf,k=%d,lk=%ld", df, k, lk);
    LOGE("CrazyMoJNI", "接收Java传递而来的参数：f=%f b=%d,c=%c", f, b, c);
    ///LOGE("CrazyMoJNI","接收Java传递而来的参数：df=%lf,k=%d,lk=%ld,f=%f b=%d,c=%c",df,k,lk,f ,b,c);//这是个LOG库里的天坑，这样子输出有问题
}
extern "C"
JNIEXPORT void JNICALL
Java_com_crazymo_ndk_jni_JNIHelper_sendBasicRefType(JNIEnv *env, jobject instance, jobject df,
                                                    jobject k, jobject lk, jobject f, jobject b,
                                                    jobject c) {
    jclass dfclz = env->GetObjectClass(df);

    LOGE("CrazyMoJNI", "接收Java传递而来的基本类型的封装类型参数：df=%lf,k=%d,lk=%ld",
         *df, *k, *lk);
    LOGE("CrazyMoJNI", "接收Java传递而来的基本类型的封装类型参数：f=%f b=%d,c=%c", *f, *b, *c);

}

extern "C"
JNIEXPORT void JNICALL
Java_com_crazymo_ndk_jni_JNIHelper_sendString(JNIEnv *env, jobject instance, jstring s_,
                                              jobjectArray strArr) {
    const char *s = env->GetStringUTFChars(s_, 0);//得到对应的jstring字符串指针，会自动产生局部引用
    LOGE("CrazyMoJNI", "Java 传递过来的字符串为：%s", s);
    ///LOGE("CrazyMoJNI","Java 传递过来的字符串为：%c",*s);这样子输出的是首字符
    int32_t arr_len = env->GetArrayLength(strArr);
    LOGE("CrazyMoJNI", "Java 传递过来的字符串数组长度为：%d", arr_len);
    for (int i = 0; i < arr_len; i++) {
        jstring str = static_cast<jstring >(env->GetObjectArrayElement(strArr, i));
        const char *c_str = env->GetStringUTFChars(str, 0);
        LOGE("CrazyMoJNI", "遍历字符串数组：%s", c_str);
        env->ReleaseStringUTFChars(str, c_str);//主动释放局部引用
    }
    env->ReleaseStringUTFChars(s_, s);//主动释放局部引用
}

extern "C"
JNIEXPORT void JNICALL
Java_com_crazymo_ndk_jni_JNIHelper_sendBean(JNIEnv *env, jobject instance, jobject bean) {

    //反射调用java方法
    //1、获取java对应的class对象
    jclass beanClz = env->GetObjectClass(bean);
    //2、找到要调用的方法
    // 参数3： 签名
    //getBlogAdrr方法
    jmethodID getAddr = env->GetMethodID(beanClz, "getBlogAdrr", "()Ljava/lang/String;");
    //3、调用
    jstring addr = static_cast<jstring>(env->CallObjectMethod(bean, getAddr));
    ///jstring addr=(jstring)(env->CallObjectMethod(bean,getAddr));
    const char *blogAddr = env->GetStringUTFChars(addr, 0);//得到对应的jstring字符串指针，会自动产生局部引用
    LOG("C++ 调用Java getBlogAdrr方法:%s", blogAddr);


    jmethodID setI = env->GetMethodID(beanClz, "setId", "(I)V");///GetMethodIDX接收的引用类型的字节码
    env->CallVoidMethod(bean, setI, 2008);//调用无返回值的非静态方法
    jmethodID getI = env->GetMethodID(beanClz, "getId", "()I");
    jint id = env->CallIntMethod(bean, getI);///CallXxxxMethod传递的是引用类型的jobject
    LOG("C++ 调用Java setId更新为:%d", id);
    //调用静态方法，无论是私有方法还是公有方法
    jmethodID showMethod = env->GetStaticMethodID(beanClz, "show", "()V");
    env->CallStaticVoidMethod(beanClz, showMethod, "()V");//传递过来的是jclass

    //在Jni创建java对象：
    jclass newBeanClz = env->FindClass("com/crazymo/ndk/bean/Blog");
    //反射创建对象
    //1、获得类的构造方法,方法名固定为<init>,后面的签名为对应构造方法的签名
    jmethodID constuct = env->GetMethodID(newBeanClz, "<init>", "(Ljava/lang/String;I)V");
    //创建java字符串
    jstring newAddr = env->NewStringUTF("在JNI赋值的Addr");
    const char *c_newAddr = env->GetStringUTFChars(newAddr, 0);
    LOG("C++ 调用Java创建BlogAddr字符串:%s", c_newAddr);
    //2、调用构造方法 创建对象
    jobject bean2 = env->NewObject(newBeanClz, constuct, newAddr, 999);//这里传入c_newAddr则会直接报错
    jmethodID show2 = env->GetMethodID(beanClz, "show2", "()V");///GetMethodIDX接收的引用类型的字节码
    env->CallVoidMethod(bean2, show2);

    //修改属性值
    jfieldID fileId = env->GetFieldID(newBeanClz, "id", "I");
    env->SetIntField(bean2, fileId, 66666);
    jstring newAddr2 = env->NewStringUTF("在JNI赋值的Addr6666");
    jfieldID newAddrField = env->GetFieldID(newBeanClz, "blogAdrr", "Ljava/lang/String;");
    env->SetObjectField(bean2, newAddrField, newAddr2);
    jint id2 = env->GetIntField(bean2, fileId);
    jstring blogAddr2 = static_cast<jstring>(env->GetObjectField(bean2, newAddrField));
    const char *c_blog2 = env->GetStringUTFChars(blogAddr2, 0);
    LOG("C++ 获取Java的成员变量id=%d，blogAdrr=%s", id2, c_blog2);
    env->CallVoidMethod(bean2, show2);

    env->ReleaseStringUTFChars(newAddr, c_newAddr);
    env->ReleaseStringUTFChars(addr, blogAddr);
    env->ReleaseStringUTFChars(blogAddr2, c_blog2);
    //后面不再使用bean2了 ，我希望它引用对象占用的内存可以被马上回收
    env->DeleteLocalRef(bean2);
    env->DeleteLocalRef(newAddr2);
}

extern "C"
JNIEXPORT void JNICALL Java_com_crazymo_ndk_jni_JNIHelper_sendArrays(JNIEnv *env, jobject instance,
                                                                     jintArray idArry,
                                                                     jobjectArray blogArr,
                                                                     jobject list) {
    //读取整形数组
    int32_t id_len = env->GetArrayLength(idArry);
    LOG("id数组的长度：%d", id_len);
    if (id_len > 0) {
        jint *id_p = env->GetIntArrayElements(idArry, NULL);
        for (int i = 0; i < id_len; ++i) {
            LOG("id数组的元素：%d", *(id_p + i));
        }
    }
    //读取对象数组
    int32_t blog_len = env->GetArrayLength(blogArr);
    LOG("Blog对象数组的长度：%d", blog_len);
    for (int i = 0; i < blog_len; ++i) {
        jobject blog_obj = env->GetObjectArrayElement(blogArr, i);
        jclass blog_clz = env->GetObjectClass(blog_obj);
        ///jclass blog_clz=env->FindClass("com/crazymo/ndk/jni/JNIHelper");
        jmethodID showMethod = env->GetMethodID(blog_clz, "show2", "()V");
        env->CallVoidMethod(blog_obj, showMethod);
    }
}

extern "C"
JNIEXPORT void JNICALL
Java_com_crazymo_ndk_jni_JNIHelper_sendList(JNIEnv *env, jobject instance, jobject listobj,
                                                  jobject map_obj) {
    //访问List
    jclass list_clz = env->GetObjectClass(
            listobj);//两种形式都可以jclass list_clz=env->FindClass("java/util/List");
    jclass list_clz2 = env->FindClass("java/util/ArrayList");//可以写ArrayList，反正是要反射调用get和size函数
    jmethodID get_methd = env->GetMethodID(list_clz, "get", "(I)Ljava/lang/Object;");
    jmethodID size_methd = env->GetMethodID(list_clz2, "size", "()I");
    jint list_len = env->CallIntMethod(listobj, size_methd);
    LOG("List集合的size：%d", list_len);
    for (int i = 0; i < list_len; i++) {
        jstring str = static_cast<jstring >(env->CallObjectMethod(listobj, get_methd, i));
        const char *s = env->GetStringUTFChars(str, 0);
        LOG("List集合的size：%s", s);
        env->ReleaseStringUTFChars(str, s);
    }
}

extern "C"
JNIEXPORT void JNICALL
Java_com_crazymo_ndk_jni_JNIHelper_sendMap(JNIEnv *env, jobject instance,jobject map_obj) {
    /**
     * //keySet方式遍历
    Set<Person> set=map.keySet();
    Iterator<Person> it=set.iterator();//得到Set集合的迭代器
    while(it.hasNext()){
        Object key=it.next();//通过迭代器获取键
        String value=map.get(key);//map根据键获取对应的值
        System.out.println(key.toString()+":"+value);
    }
     */
    jclass map_clz = env->GetObjectClass(map_obj);//根据Map的实例获取对应的Class字节码对象
    jmethodID size_map_methd = env->GetMethodID(map_clz, "size", "()I");//获取Map字节码对象反射获取size()方法ID
    jint map_len = env->CallIntMethod(map_obj, size_map_methd);//通过Map实例调用size方法
    LOG("Map映射的size：%d", map_len);
    jmethodID keyset_method = env->GetMethodID(map_clz, "keySet",
                                               "()Ljava/util/Set;");//获取Map字节码对象获取Set<K> keySet()方法
    jobject keyset_obj = env->CallObjectMethod(map_obj,
                                               keyset_method);//调用keySet方法得到键的Set集合<=>set=map.keySet()
    jclass keyset_clz = env->GetObjectClass(
            keyset_obj);///jclass keyset_clz=env->FindClass("java/util/Set");
    jmethodID iterator_methd = env->GetMethodID(keyset_clz, "iterator", "()Ljava/util/Iterator;");
    jobject iterator_obj = env->CallObjectMethod(keyset_obj, iterator_methd);//等价于调用了set.iterator()
    jclass iterator_clz = env->GetObjectClass(iterator_obj);
    jmethodID hasnext_method = env->GetMethodID(iterator_clz, "hasNext", "()Z");
    jboolean hasnext = env->CallBooleanMethod(iterator_obj, hasnext_method);
    LOG("C++ Map的容量--hasNext=%d", hasnext);
    while (hasnext) {
        jmethodID next_method = env->GetMethodID(iterator_clz, "next",
                                                 "()Ljava/lang/Object;");//获取Iterator的next方法
        jmethodID getv_methd = env->GetMethodID(map_clz, "get",
                                                "(Ljava/lang/Object;)Ljava/lang/Object;");
        jobject key_obj = env->CallObjectMethod(iterator_obj, next_method);//调用it.next得到Key
        jstring value_obj = static_cast<jstring>(env->CallObjectMethod(map_obj, getv_methd,
                                                                       key_obj));
        const char *c_blog2 = env->GetStringUTFChars(value_obj, 0);
        LOG("C++ Map遍历的元素：%s", c_blog2);
        hasnext = env->CallBooleanMethod(iterator_obj, hasnext_method);
        env->ReleaseStringUTFChars(value_obj,c_blog2);
    }
    env->DeleteLocalRef(map_clz);
    env->DeleteLocalRef(keyset_obj);
    env->DeleteLocalRef(keyset_clz);
    env->DeleteLocalRef(iterator_obj);
    env->DeleteLocalRef(iterator_clz);
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_crazymo_ndk_jni_JNIHelper_getBasicData(JNIEnv *env, jobject instance) {
    return 888888;
}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_crazymo_ndk_jni_JNIHelper_getStringData(JNIEnv *env, jobject instance) {
    char* tmpstr = "从JNI发送到Java的字符串";
    jstring rtstr = env->NewStringUTF(tmpstr);
    return rtstr;
}

extern "C"
JNIEXPORT jintArray JNICALL
Java_com_crazymo_ndk_jni_JNIHelper_getArrayData(JNIEnv *env, jobject instance,jint len) {
    //主要调用 set<Type>ArrayRegion 来填充数据，其他数据类型类似操作
    jintArray array = NULL;
    if (len > 0) {
        array = env->NewIntArray(len);
        jint buf[len];
        for (int i = 0; i < len; ++i) {
            buf[i] = i * 2;
        }
        // 使用 setIntArrayRegion 来赋值
        env->SetIntArrayRegion(array, 0, len, buf);
        return array;
    }
    return array;
}

extern "C"
JNIEXPORT jobjectArray JNICALL
Java_com_crazymo_ndk_jni_JNIHelper_getObject(JNIEnv *env, jobject instance) {
    jobjectArray result_arr = NULL;
    jint len = 2;
    jclass blog_clz = env->FindClass("com/crazymo/ndk/bean/Blog");//获取Blog对应的jclass
    result_arr = env->NewObjectArray(len, blog_clz, NULL);

    for (int i = 0; i < len; ++i) {

        jmethodID construct_methd = env->GetMethodID(blog_clz, "<init>", "(Ljava/lang/String;I)V");
        //1、创建java字符串
        jstring newAddr = env->NewStringUTF("在C++创建Java Blog对象");
        const char *c_newAddr = env->GetStringUTFChars(newAddr, 0);
        //2、调用构造方法 创建对象
        jobject blog = env->NewObject(blog_clz, construct_methd, newAddr, 6666+i*100);//这里传入c_newAddr则会直接报错
        env->SetObjectArrayElement(result_arr,i,blog);
        env->ReleaseStringUTFChars(newAddr, c_newAddr);
    }
    env->DeleteLocalRef(blog_clz);
    return result_arr;
}

extern "C"
JNIEXPORT jobject JNICALL
Java_com_crazymo_ndk_jni_JNIHelper_getList(JNIEnv *env, jobject instance,jint size) {
    jclass list_clz=env->FindClass("java/util/ArrayList");
    jmethodID construct_methd=env->GetMethodID(list_clz,"<init>","()V");
    jmethodID add_method=env->GetMethodID(list_clz,"add","(Ljava/lang/Object;)Z");
    jobject list_obj=env->NewObject(list_clz,construct_methd);
    jstring newAddr = env->NewStringUTF("在C++创建Java List对象");
    for (int i = 0; i < size; i++) {
        ///const char *c_newAddr = env->GetStringUTFChars(newAddr, 0);
        ///env->CallBooleanMethod(list_obj,add_method,c_newAddr);
        env->CallBooleanMethod(list_obj,add_method,newAddr);
        //env->ReleaseStringUTFChars(newAddr, c_newAddr);
    }
    env->ReleaseStringChars(newAddr,0);
    env->DeleteLocalRef(list_clz);
    return list_obj;
}

extern "C"
JNIEXPORT jobject JNICALL
Java_com_crazymo_ndk_jni_JNIHelper_getMap(JNIEnv *env, jobject instance) {
    jclass map_clz=env->FindClass("java/util/HashMap");
    jmethodID construct_methd=env->GetMethodID(map_clz,"<init>","()V");
    jmethodID put_method=env->GetMethodID(map_clz,"put","(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
    jobject map_obj=env->NewObject(map_clz,construct_methd);
    jstring key = env->NewStringUTF("CrazyMo_");
    jstring value = env->NewStringUTF("在C++创建Java Map对象");
    env->CallObjectMethod(map_obj,put_method,key,value);
    return map_obj;
}



